using System;

namespace SeaVM.Instructions
{
    public class Jmp
    {
        public static void jmp(VM vm,int jmp_line)
        {
            if(jmp_line < 0 || jmp_line >= vm.instructions.Count)
            {
                Console.WriteLine("跳转过界！");
                vm.line = -1;
            }
            vm.line = jmp_line;
        }

        public static void jmp(VM vm,string jump_node)
        {
            if(!vm.jump_nodes.ContainsKey(jump_node))
            {
                throw new VmRunError("找不到跳转节点:"+jump_node);
            }
            int line = vm.jump_nodes[jump_node];
            jmp(vm,line);
        }

        public static Instruction Create(int line)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Jmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.Number;
            arg1.number = line;
            inst.args.Add(arg1);
            return inst;
        }
        public static Instruction Create(string jump_node)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Jmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.JumpNode;
            arg1.jump_node = jump_node;
            inst.args.Add(arg1);
            return inst;
        }

        public static Instruction Create2(int address)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Jmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.Address;
            arg1.address = address;
            inst.args.Add(arg1);
            return inst;
        }
        public static Instruction Create2(string define)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Jmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.Define;
            arg1.define = define;
            inst.args.Add(arg1);
            return inst;
        }

        public static void Run(VM vm,Instruction inst)
        {
            InstructionArg arg1 = inst.args[0];
            switch(arg1.type)
            {
                case InstructionArgType.Number:
                {
                    jmp(vm,arg1.number);
                    break;
                }
                case InstructionArgType.JumpNode:
                {
                    jmp(vm,arg1.jump_node);
                    break;
                }
                case InstructionArgType.Address:
                {
                    if(arg1.address < 0 || arg1.address >= vm.memory.size)
                    {
                        throw new VmRunError("地址越界");
                    }
                    Data data = vm.memory.Read(arg1.address);
                    if(ReferenceEquals(data,null))
                    {
                        throw new VmRunError("地址处为Nil");
                    }
                    if(data.type == DataType.Number)
                    {
                        jmp(vm,(int)data.number.value);
                        break;
                    }
                    if(data.type == DataType.String)
                    {
                        jmp(vm,data.@string.value);
                        break;
                    }
                    throw new VmRunError("地址处类型需为Number或者String");
                }
                case InstructionArgType.Define:
                {
                    if(vm.defines.ContainsKey(arg1.define))
                    {
                        int address = vm.defines[arg1.define];
                        if(address < 0 || address >= vm.memory.size)
                        {
                            throw new VmRunError("地址越界");
                        }
                        Data data = vm.memory.Read(address);
                        if(ReferenceEquals(data,null))
                        {
                            throw new VmRunError("地址处为Nil");
                        }
                        if(data.type == DataType.Number)
                        {
                            jmp(vm,(int)data.number.value);
                            break;
                        }
                        if(data.type == DataType.String)
                        {
                            jmp(vm,data.@string.value);
                            break;
                        }
                    }
                    else
                    {
                        throw new VmRunError("没有这个定义");
                    }
                    break;
                }
                default:throw new VmRunError("没有这种形式的指令");
            }
        }
    }
}